﻿// =====================================================================
// <copyright file="ArgumentHelper.cs" author="David R. Guindo">
// 	Copyright (C) 2013. All rights reserved.
// </copyright>
// =====================================================================
// This code is released under the terms of the Apache 2.0 license, 
// http://apache.org/licenses/LICENSE-2.0.html
// =====================================================================

namespace AppBlock.Core
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Text;
    using AppBlock.Core.Resources;

    /// <summary>
    ///     Helper for making assertions on arguments.
    /// </summary>
    public static class ArgumentHelper
    {
        /// <summary>
        ///     Asserts the are not equal.
        /// </summary>
        /// <typeparam name="T"> Type of the arguments. </typeparam>
        /// <param name="leftSide"> The left side. </param>
        /// <param name="rightSide"> The right side. </param>
        /// <param name="argumentName">The property name for check the value.</param>
        /// <param name="msg"> The error message. </param>
        public static void AssertAreEqual<T>(T leftSide, T rightSide, string argumentName = null, string msg = null)
        {
            if (!Equals(leftSide, rightSide))
            {
                throw ((msg == null) ? new ArgumentException() : new ArgumentException(msg));
            }
        }

        /// <summary>
        ///     Asserts the are not equal.
        /// </summary>
        /// <typeparam name="T"> Type of the arguments. </typeparam>
        /// <param name="leftSide"> The left side. </param>
        /// <param name="rightSide"> The right side. </param>
        /// <param name="argumentName">The property name for check the value.</param>
        /// <param name="msg"> The error message. </param>
        public static void AssertAreNotEqual<T>(T leftSide, T rightSide, string argumentName = null, string msg = null)
        {
            if (Equals(leftSide, rightSide))
            {
                throw ((msg == null) ? new ArgumentException() : new ArgumentException(msg));
            }
        }

        /// <summary>
        ///     Asserts the contains. It means that the collection has to contain an instance of the node.
        /// </summary>
        /// <typeparam name="T"> Type of the arguments. </typeparam>
        /// <param name="collection"> The collection. </param>
        /// <param name="node"> The node. </param>
        /// <param name="argumentName">The property name for check the value.</param>
        /// <param name="msg"> The error message. </param>
        public static void AssertContains<T>(ICollection<T> collection, T node, string argumentName = null,
            string msg = null)
        {
            AssertIsNotNull(collection, "Collection is NULL");
            if (!((Collection<T>) collection).Contains(node))
            {
                throw ((msg == null) ? new ArgumentException() : new ArgumentException(msg));
            }
        }

        /// <summary>
        ///     Asserts the contains. It means that the collection has to contain an instance of the node.
        /// </summary>
        /// <typeparam name="T"> Type of the arguments. </typeparam>
        /// <param name="collection"> The collection. </param>
        /// <param name="predicate"> The predicate. </param>
        /// <param name="argumentName">The property name for check the value.</param>
        /// <param name="msg"> The error message. </param>
        public static void AssertContains<T>(ICollection<T> collection, Func<T, bool> predicate,
            string argumentName = null, string msg = null)
        {
            AssertIsNotNull(collection, "Collection is NULL");
            if (!collection.Any(predicate))
            {
                throw ((msg == null) ? new ArgumentException() : new ArgumentException(msg));
            }
        }

        /// <summary>
        ///     Asserts the contains. It means that the collection has to contain an instance of the node.
        /// </summary>
        /// <typeparam name="T"> Type of the arguments. </typeparam>
        /// <param name="collection"> The collection. </param>
        /// <param name="argumentName">The property name for check the value.</param>
        /// <param name="msg"> The error message. </param>
        public static void AssertHasAny<T>(ICollection<T> collection, string argumentName = null, string msg = null)
        {
            AssertIsNotNull(collection, "Collection is NULL");

            if (!collection.Any())
            {
                throw ((msg == null) ? new ArgumentException() : new ArgumentException(msg));
            }
        }

        /// <summary>
        ///     Asserts the has any.
        /// </summary>
        /// <typeparam name="T"> </typeparam>
        /// <param name="argumentName">The property name for check the value.</param>
        /// <param name="collection"> The collection. </param>
        /// <param name="msg"> The MSG. </param>
        public static void AssertHasAny<T>(IEnumerable<T> collection, string argumentName = null, string msg = null)
        {
            AssertIsNotNull(collection, "Collection is NULL");

            if (!collection.Any())
            {
                throw ((msg == null) ? new ArgumentException() : new ArgumentException(msg));
            }
        }

        /// <summary>
        ///     Asserts the is false.
        /// </summary>
        /// <param name="value"> The value. </param>
        /// <param name="argumentName">The property name for check the value.</param>
        /// <param name="msg"> The error message. </param>
        public static void AssertIsFalse(bool value, string argumentName = null, string msg = null)
        {
            if (value)
            {
                throw ((msg == null) ? new ArgumentException() : new ArgumentException(msg));
            }
        }

        /// <summary>
        ///     Asserts the is instance of.
        /// </summary>
        /// <typeparam name="T"> </typeparam>
        /// <param name="value"> The value. </param>
        /// <param name="type"> The type. </param>
        /// <param name="argumentName">The property name for check the value.</param>
        /// <param name="msg"> The error message. </param>
        public static void AssertIsInstanceOf<T>(T value, Type type, string argumentName = null, string msg = null)
        {
            if (!value.GetType().IsInstanceOfType(type))
            {
                throw ((msg == null) ? new ArgumentException() : new ArgumentException(msg));
            }
        }

        /// <summary>
        ///     Asserts that <paramref name="value" /> <see cref="Nullable{T}.HasValue" /> and <c>is not</c>
        ///     <see cref="Guid.Empty" />.
        /// </summary>
        /// <param name="value">
        ///     The <see cref="Nullable{T}" /> of <see cref="Guid" /> whose value will be asserted as
        ///     <c>not empty</c> .
        /// </param>
        /// <param name="message"> The message to select (and decorate) raised exception. </param>
        /// <exception cref="ArgumentException">
        ///     Raised whenever
        ///     <paramref name="value" />
        ///     were considered as
        ///     <c>empty</c>
        ///     and
        ///     <paramref name="message" />
        ///     were
        ///     <see cref="string.IsNullOrWhiteSpace" />
        ///     .
        /// </exception>
        /// <exception cref="ArgumentNullException">
        ///     Raised whenever
        ///     <paramref name="value" />
        ///     were considered as
        ///     <c>empty</c>
        ///     and
        ///     <paramref name="message" />
        ///     were not
        ///     <see cref="string.IsNullOrWhiteSpace" />
        ///     .
        /// </exception>
        public static void AssertIsNotEmpty(Guid? value, string message = null)
        {
            AssertIsNotEmpty(value ?? Guid.Empty, message);
        }

        /// <summary>
        ///     Asserts that <paramref name="value" />
        ///     <c>is not</c>
        ///     <see cref="Guid.Empty" />.
        /// </summary>
        /// <param name="value">The <see cref="Guid" /> whose value will be asserted as <c>not empty</c> .</param>
        /// <param name="argumentName">Name of the argument.</param>
        /// <param name="message">The message to select (and decorate) raised exception.</param>
        /// <exception cref="ArgumentException">
        ///     Raised whenever
        ///     <paramref name="value" />
        ///     were considered as
        ///     <c>empty</c>
        ///     and
        ///     <paramref name="message" />
        ///     were
        ///     <see cref="string.IsNullOrWhiteSpace" />
        ///     .
        /// </exception>
        /// <exception cref="ArgumentNullException">
        ///     Raised whenever
        ///     <paramref name="value" />
        ///     were considered as
        ///     <c>empty</c>
        ///     and
        ///     <paramref name="message" />
        ///     were not
        ///     <see cref="string.IsNullOrWhiteSpace" />
        ///     .
        /// </exception>
        public static void AssertIsNotEmpty(Guid value, string argumentName = null, string message = null)
        {
            if (Guid.Empty.Equals(value))
            {
                throw (string.IsNullOrWhiteSpace(message) ? new ArgumentException() : new ArgumentNullException(message));
            }
        }

        /// <summary>
        ///     Asserts the is not null.
        /// </summary>
        /// <typeparam name="T"> Type of the arguments. </typeparam>
        /// <param name="value"> The value. </param>
        /// <param name="argumentName">The property name for check the value.</param>
        /// <param name="msg"> The error message. </param>
        public static void AssertIsNotNull<T>(T value, string argumentName = null, string msg = null) where T : class
        {
            if (value == null)
            {
                throw ((msg == null) ? new ArgumentNullException(argumentName) : new ArgumentNullException(msg));
            }
        }

        /// <summary>
        ///     Asserts the is not null.
        /// </summary>
        /// <typeparam name="T"> Type of the arguments. </typeparam>
        /// <param name="expressions">The property or object to validate is if null or not.</param>
        public static void IsNotNull(params Expression<Func<object>>[] expressions)
        {
            StringBuilder expname = new StringBuilder();
            foreach (var exp in expressions)
            {
                object result = null;
                try
                {
                    var comp = exp.Compile();
                    result = comp.Invoke();
                }
                catch
                {
                    //if parent object is null Invoke throw error
                }

                if (result == null)
                {
                    if (exp.Body is UnaryExpression)
                    {
                        expname.AppendFormat("{0},", FilterName(exp));
                        //var unaryExpression = (UnaryExpression)exp.Body;
                        //memberExpression = (MemberExpression)unaryExpression.Operand;
                    }
                    else
                    {
                        expname.AppendFormat("{0},", ((MemberExpression) exp.Body).Member.Name);
                    }
                }
            }

            if (expname.Length > 0)
            {
                expname.Remove(expname.Length - 1, 1);
                throw ErrorMsg.ArgumentHelper_IsNotNull(expname.ToString());
            }
        }

        private static string FilterName(Expression<Func<object>> exp)
        {
            var str = exp.ToString();
            return str.Substring(str.IndexOf(").") + 2).TrimEnd(')');
        }

        /// <summary>
        ///     Asserts the is not null.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="argumentName">The property name for check the value.</param>
        /// <param name="msg">The error message.</param>
        public static void AssertIsNotNullOrWhiteSpace(string value, string argumentName = null, string msg = null)
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                throw ((msg == null) ? new ArgumentNullException() : new ArgumentNullException(msg));
            }
        }

        /// <summary>
        ///     Asserts the is true.
        /// </summary>
        /// <param name="value"> The value. </param>
        /// <param name="argumentName">The property name for check the value.</param>
        /// <param name="msg"> The error message. </param>
        public static void AssertIsTrue(bool value, string argumentName = null, string msg = null)
        {
            if (!value)
            {
                throw ((msg == null) ? new ArgumentException() : new ArgumentException(msg));
            }
        }

        /// <summary>
        ///     It indicates if <paramref name="value" /> <see cref="Nullable{T}.HasValue" />
        ///     and <c>is not</c> <see cref="IEquatable{T}.Equals(T)" /> to
        ///     <see cref="Guid.Empty" />.
        /// </summary>
        /// <param name="value"> The <see cref="Nullable{T}" /> of <see cref="Guid" /> whose actual value will be evaluated. </param>
        /// <returns>
        ///     It returns <c>true</c> whenever <paramref name="value" /> <see cref="Nullable{T}.HasValue" /> and <c>is not</c>
        ///     <see
        ///         cref="IEquatable{T}.Equals(T)" />
        ///     to <see cref="Guid.Empty" /> ; otherwise it returns <c>false</c> .
        /// </returns>
        public static bool IsNotEmpty(this Guid? value)
        {
            return (value.HasValue && (value.Value != Guid.Empty));
        }

        /// <summary>
        ///     It indicates if <paramref name="value" /> <c>is not</c>
        ///     <see cref="IEquatable{T}.Equals(T)" /> to <see cref="Guid.Empty" />.
        /// </summary>
        /// <param name="value"> The <see cref="Guid" /> whose actual value will be evaluated. </param>
        /// <returns>
        ///     It returns <c>true</c> whenever <paramref name="value" /> <c>is not</c> <see cref="IEquatable{T}.Equals(T)" /> to
        ///     <see
        ///         cref="Guid.Empty" />
        ///     ; otherwise it returns <c>false</c> .
        /// </returns>
        public static bool IsNotEmpty(this Guid value)
        {
            return (value != Guid.Empty);
        }
    }
}